/**
 * @file stm32f4xx_flash_driver.c
 * @brief STM32F4xx CycloneBOOT flash driver
 *
 * @section License
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 *
 * Copyright (C) 2010-2021 Oryx Embedded SARL. All rights reserved.
 *
 * This file is part of CycloneBOOT Open.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * @author Oryx Embedded SARL (www.oryx-embedded.com)
 * @version 1.0.1
 **/

//Switch to the appropriate trace level
#define TRACE_LEVEL IAP_TRACE_LEVEL

//Dependencies
#include "stm32f4xx.h"
#include "stm32f4xx_hal.h"
#include "stm32f4xx_flash_driver.h"
#include "stdio.h"

//STM32F4XX flash driver private related functions
int32_t flashGetSector(uint32_t address);
error_t flashGetCurrentBank(uint8_t *fBankID);

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////


/**
 * @brief STM32F4XX Flash driver
 **/

const FlashDriver flashDriver =
{
   FLASH_DRIVER_VERSION,
   FLASH_ADDR,
   FLASH_SIZE,
   FLASH_WRITE_TYPE_WORD,
   {
      {FLASH_BANK_1_ID, FLASH_BANK_1_SIZE, FLASH_BANK_1_ADDR},
      {FLASH_BANK_2_ID, FLASH_BANK_2_SIZE, FLASH_BANK_2_ADDR}
   },
   {
      {FLASH_SECTOR_0,  FLASH_SECTOR_0_SIZE,   FLASH_SECTOR_0_ADDR},
      {FLASH_SECTOR_1,  FLASH_SECTOR_1_SIZE,   FLASH_SECTOR_1_ADDR},
      {FLASH_SECTOR_2,  FLASH_SECTOR_2_SIZE,   FLASH_SECTOR_2_ADDR},
      {FLASH_SECTOR_3,  FLASH_SECTOR_3_SIZE,   FLASH_SECTOR_3_ADDR},
      {FLASH_SECTOR_4,  FLASH_SECTOR_4_SIZE,   FLASH_SECTOR_4_ADDR},
      {FLASH_SECTOR_5,  FLASH_SECTOR_5_SIZE,   FLASH_SECTOR_5_ADDR},
      {FLASH_SECTOR_6,  FLASH_SECTOR_6_SIZE,   FLASH_SECTOR_6_ADDR},
      {FLASH_SECTOR_7,  FLASH_SECTOR_7_SIZE,   FLASH_SECTOR_7_ADDR},

      {FLASH_SECTOR_12, FLASH_SECTOR_12_SIZE,  FLASH_SECTOR_12_ADDR},
      {FLASH_SECTOR_13, FLASH_SECTOR_13_SIZE,  FLASH_SECTOR_13_ADDR},
      {FLASH_SECTOR_14, FLASH_SECTOR_14_SIZE,  FLASH_SECTOR_14_ADDR},
      {FLASH_SECTOR_15, FLASH_SECTOR_15_SIZE,  FLASH_SECTOR_15_ADDR},
      {FLASH_SECTOR_16, FLASH_SECTOR_16_SIZE,  FLASH_SECTOR_16_ADDR},
      {FLASH_SECTOR_17, FLASH_SECTOR_17_SIZE,  FLASH_SECTOR_17_ADDR},
      {FLASH_SECTOR_18, FLASH_SECTOR_18_SIZE,  FLASH_SECTOR_18_ADDR},
      {FLASH_SECTOR_19, FLASH_SECTOR_19_SIZE,  FLASH_SECTOR_19_ADDR},

   }
};


/**
* @brief Initialize Flash.
* @return Error code
**/

error_t flashInit(void)
{
   error_t error;
   uint8_t fCurrentBankID;

   //Debug message
   printf("Initializing STM32F4xx flash driver...\n");

   //Initialize FLASH flags
   //(Patch to fix stm32 hal library wrong initial flash flags issue)
   FLASH_WaitForLastOperation((uint32_t)50000U);

   //Get current used flash bank
   error = flashGetCurrentBank(&fCurrentBankID);
   //Is any error?
   if (error)
	  return error;

   //Debug message
   if(fCurrentBankID == FLASH_BANK_1_ID)
	   printf("Running on flash bank 1...\r\n");
   else
	   printf("Running on flash bank 2...\r\n");

   //Successfull process
   return NO_ERROR;
}


/**
* @brief Get Flash start address.
* @return Error code
**/

error_t flashGetStartAddr(uint32_t *startAddr)
{
   //Check parameter validity
   if(startAddr == NULL)
      return ERROR_INVALID_PARAMETER;

   //Set flash start address (always second bank)
   *startAddr = FLASH_BANK_1_ADDR;

   //Successful process
   return NO_ERROR;
}


/**
* @brief Get Flash bank size.
* @return Size of the flash bank
**/

size_t flashGetBankSize(void)
{
   //Return flash bank size
   return flashDriver.banks[FLASH_BANK_1_ID].size;
}


/**
* @brief Write a byte into Flash memory at the given address.
* @param[in] address Flash address
* @param[in] byte Byte to be written
* @return Error code
**/

error_t flashWriteByte(uint32_t address, uint8_t byte)
{
   FlashBanks *banks;

   //Point to flash driver banks
   banks = (FlashBanks*)&flashDriver.banks;

   //Check address validty
   if(address < banks[FLASH_BANK_2_ID].addr ||
      address > (banks[FLASH_BANK_2_ID].addr +
      banks[FLASH_BANK_2_ID].size - sizeof(uint32_t)))
      return ERROR_INVALID_PARAMETER;

   //TODO write byte in flash here

   return ERROR_NOT_IMPLEMENTED;
}


/**
* @brief Write a half word into Flash memory at the given address.
* @param[in] address Flash address
* @param[in] halfWord 16-bit Half Word to be written
* @return Error code
**/

error_t flashWriteHalfWord(uint32_t address, uint16_t halfWord)
{
   FlashBanks *banks;

   //Point to flash driver banks
   banks = (FlashBanks*)&flashDriver.banks;

   //Check address validty
   if(address < banks[FLASH_BANK_2_ID].addr ||
      address > (banks[FLASH_BANK_2_ID].addr +
      banks[FLASH_BANK_2_ID].size - sizeof(uint32_t)))
      return ERROR_INVALID_PARAMETER;

   //TODO write half word in flash here

   return ERROR_NOT_IMPLEMENTED;
}


/**
* @brief Write a word into Flash memory at the given address.
* @param[in] address Flash address
* @param[in] word 32-bit Word to be written
* @return Error code
**/

error_t flashWriteWord(uint32_t address, uint32_t word)
{
   error_t error;
   int32_t sector;
   uint32_t sectorError;
   HAL_StatusTypeDef status;
   FLASH_EraseInitTypeDef EraseInitStruct;
   uint8_t fCurrentBankID;



   //Retrieve current Flash sector
   sector = flashGetSector(address);

  // printf("\n sector:%d\n",(int)sector);
   //Check whether the address match the beginning of a Flash sector.
   //If this is the case then the flash sector must be erased before any write operation
   if(sector >= 0)
   {
      //Get current used flash bank
      error = flashGetCurrentBank(&fCurrentBankID);
     // printf("\r\n fCurrentBankID:%d\r\n",fCurrentBankID);
      //Is any error?
      if (error)
         return error;

      //Debug message
     // printf("Erasing Flash sector %x...\r\n", (int)sector);

      //Start of exception handling block
      do
      {
         //Allow access to Flash control registers and user False
         status = HAL_FLASH_Unlock();
         //Is any error?
         if (status != HAL_OK)
         {
            //Debug message
        	 printf("Flash Control Register unlock failed!\r\n");
            break;
         }

         //Initialize FLASH flags
         status = FLASH_WaitForLastOperation((uint32_t)50000U);
         //Is any error?
         if(status != HAL_OK)
         {
            //Debug message
        //	 printf("Initialize FLASH flags...\r\n");
         }

         //Set flash erase parameters
         EraseInitStruct.TypeErase = FLASH_TYPEERASE_SECTORS;
         EraseInitStruct.VoltageRange = FLASH_VOLTAGE_RANGE_3;
         EraseInitStruct.Sector = sector;
         EraseInitStruct.NbSectors = 1;

         //Erase the specified Flash sector
         status = HAL_FLASHEx_Erase(&EraseInitStruct, &sectorError);
         //Is any error?
         if(status != HAL_OK)
         {
            //Debug message
        	 printf("Failed to erase flash sector %d, error = 0x%08X!\n", (int)sector, (int)sectorError);
         }

         //Disable the Flash option control register access (recommended to protect
         //the option Bytes against possible unwanted operations)
         if(HAL_FLASH_Lock() != HAL_OK)
         {
            //Debug message
        	 printf("Flash Control Register lock failed!\n");
         }
      }while(0);
   }
   else
   {
      //Erase is not needed
      status = HAL_OK;
   }

   //Check status code
   if(status == HAL_OK)
   {
      //Start of exception handling block
      do
      {
         //Allow access to Flash control registers and user False
         status = HAL_FLASH_Unlock();
         //Is any error?
         if (status != HAL_OK)
         {
            //Debug message
        	 printf("Flash Control Register unlock failed!\r\n");
            break;
         }

         //Program the 32-bit word to Flash memory
         status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, word);
         //Is any error?
         if(status != HAL_OK)
         {
            //Debug message
            printf("Failed to write word \"0x%08X\" at 0x%08X!\n", (int)word, (int)address);
         }

         //Disable the Flash option control register access (recommended to protect
         //the option Bytes against possible unwanted operations)
         if(HAL_FLASH_Lock() != HAL_OK)
         {
            //Debug message
            printf("Flash Control Register lock failed!\r\n");
         }
      }while(0);
   }

   //Return status code
   return (status == HAL_OK) ? NO_ERROR : ERROR_WRITE_FAILED;
}

error_t MoreFlashWrite(uint32_t start_address, void *data, size_t length)
{
   error_t error;
   size_t i;
   size_t nbWords;
   uint32_t *p;

   //Initialize variables
   error = NO_ERROR;





   //Retrieve the number of words inside data buffer to be written
   nbWords = length / sizeof(uint32_t);
   //Point to the first word
   p = (uint32_t*) data;
   //Debug message
  // printf("\r\nWritting data (%d Words) in flash at 0x%08X...\r\n", (int)nbWords, (int)start_address);
   //Loop through data words by words
   for(i = 0; i < nbWords && error == NO_ERROR; i++)
   {
      //Is remaining bytes bigger than a word (4 bytes)?
      if((length - i*sizeof(uint32_t)) >= sizeof(uint32_t))
      {
         //Program the 32-bit word to Flash memory
         error = flashWriteWord((start_address + i*sizeof(uint32_t)), p[i]);
         //Is any error?
         if(error)
         {
            //Debug message
            printf("Failed to write in flash at 0x%08X!\r\n", (int) (start_address+ i*sizeof(uint32_t)));
            return error;
         }
      }
   }

   //Return status code
   return NO_ERROR;
}
/**
 * @brief Performs a flash bank swap according to the current flash bank ID.
 * If current flash bank ID match the 1st bank then it will swap on the 2nd flash bank.
 * If current flash bank ID match the 2nd bank then it will swap on the 1st flash bank.
 * @return Error code
 **/

error_t flashSwapBank(void)
{
   error_t error;
   HAL_StatusTypeDef status;
   uint8_t fCurrentBankID;
   FLASH_AdvOBProgramInitTypeDef AdvOBInit;

   //Debug message
   printf("Swaping device flash bank...\r\n");
   
   //Get current flash bank ID
   error = flashGetCurrentBank(&fCurrentBankID);
   //Is any error?
   if(error)
      return error;

   //Start of exception handling block
   do
   {
      //Allow access to Flash control registers and user False
      status = HAL_FLASH_Unlock();
      //Is any error?
      if (status != HAL_OK)
      {
         //Debug message
         printf("Flash Control Register unlock failed!\r\n");
         break;
      }

      //Start of exception handling block
      do
      {
         //Allow Access to option bytes sector
         status = HAL_FLASH_OB_Unlock();
         //Is any error?
         if (status != HAL_OK)
         {
            //Debug message
            printf("Flash Option Control Registers unlock failed!\r\n");
            break;
         }

         //Get the Dual boot configuration status
         AdvOBInit.OptionType = OPTIONBYTE_BOOTCONFIG;
         HAL_FLASHEx_AdvOBGetConfig(&AdvOBInit);

         //Swap in flash bank 2
         if(fCurrentBankID == FLASH_BANK_1_ID)
         {
            //Debug message
            printf("Swaping from flask bank 1 to flash bank 2...\r\n");

            //Configure option bytes to swap on flash bank 2
            AdvOBInit.BootConfig = OB_DUAL_BOOT_ENABLE;
         }
         //Swap in flash bank 1
         else
         {
            //Debug message
        	 printf("Swaping from flask bank 2 to flash bank 1...\r\n");

            //Configure option bytes to swap on flash bank 1
            AdvOBInit.BootConfig = OB_DUAL_BOOT_DISABLE;
         }

         //Start of exception handling block
         do
         {
            //Start Advanced Option Bytes Programming
            status = HAL_FLASHEx_AdvOBProgram (&AdvOBInit);
            //Is any error?
            if (status != HAL_OK)
            {
               //Debug message
               printf("Advanced Option byte programming failed!\r\n");
               break;
            }

            //Launch the option byte loading
            status = HAL_FLASH_OB_Launch();
            //Is any error?
            if (status != HAL_OK)
            {
               //Debug message
               printf("Option byte loading failed!\r\n");
            }
         }while(0);

         //Prevent Access to option bytes sector
         if(HAL_FLASH_OB_Lock() != HAL_OK)
         {
            //Debug message
            printf("Flash Option Control Register lock failed!\r\n");
         }
      }while(0);

      //Disable the Flash option control register access (recommended to protect
      //the option Bytes against possible unwanted operations)
      if(HAL_FLASH_Lock() != HAL_OK)
      {
         //Debug message
         printf("Flash Control Register lock failed!\r\n");
      }
   }while(0);

   //Return status code
   return (status == HAL_OK)? NO_ERROR : ERROR_FAILURE;
}


/**
 * @brief Initiates a system reset request to reset the MCU.
 **/

void systemReset(void)
{
   //Initiates a system reset request to reset the MCU
   HAL_NVIC_SystemReset();
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////


/**
 * @brief Retrieve the Flash sector that matches the specified address
 * @param[in] address 32-bit address
 * @return Flash sector
 **/

int32_t flashGetSector(uint32_t address)
{
   int32_t sectorID;
   uint16_t i;
   FlashSectors *sectors;

   //Initialize sector ID
   sectorID = -1;

   //Point to flash driver sectors
   sectors = (FlashSectors*)&flashDriver.sectors;

   //Loop through flash driver sectors list
   for(i = 0; i < FLASH_SECTORS_NUMBER; i++)
   {
      //Is address match the start address of a sector?
      if(address == sectors[i].addr)
      {
         //Save sector ID
         sectorID = sectors[i].id;
         break;
      }
   }

   //Return sector ID
   return sectorID;
}


/**
 * @brief Get current used flash bank (FLASH_BANK_1_ID or FLASH_BANK_2_ID).
 * @param[out] fBankID Pointeur to the flash bank ID to be retrieved
 * @return Status code
 **/

error_t flashGetCurrentBank(uint8_t *fBankID)
{
   //FLASH_OBProgramInitTypeDef OBInit;
   FLASH_AdvOBProgramInitTypeDef AdvOBInit;
   HAL_StatusTypeDef status;

   //Check parameters validity
   if(fBankID == NULL)
      return ERROR_INVALID_PARAMETER;

      //Start of exception handling block
   do
   {
      //Allow access to Flash control registers and user False
      status = HAL_FLASH_Unlock();
      //Is any error?
      if (status != HAL_OK)
      {
         //Debug message
         printf("Flash Control Register unlock failed!\r\n");
         break;
      }

      //Start of exception handling block
      do
      {
         //Allow Access to option bytes sector
         status = HAL_FLASH_OB_Unlock();
         //Is any error?
         if (status != HAL_OK)
         {
            //Debug message
            printf("Flash Option Control Registers unlock failed!\r\n");
            break;
         }

         //Get the Dual boot configuration status
         AdvOBInit.OptionType = OPTIONBYTE_BOOTCONFIG;
         HAL_FLASHEx_AdvOBGetConfig(&AdvOBInit);

         //Running in flash bank 2
         if(((AdvOBInit.BootConfig) & (FLASH_OPTCR_BFB2)) == FLASH_OPTCR_BFB2)
         {
            *fBankID = FLASH_BANK_2_ID;
         }
         //Running in flash bank 1
         else
         {
            *fBankID = FLASH_BANK_1_ID;
         }

         //Prevent Access to option bytes sector
         if(HAL_FLASH_OB_Lock() != HAL_OK)
         {
            //Debug message
            printf("Flash Option Control Register lock failed!\r\n");
         }
      }while(0);

      //Disable the Flash option control register access (recommended to protect
      //the option Bytes against possible unwanted operations)
      if(HAL_FLASH_Lock() != HAL_OK)
      {
         //Debug message
         printf("Flash Control Register lock failed!\r\n");
      }
   }while(0);

   //Return status code
   return (status == HAL_OK)? NO_ERROR: ERROR_FAILURE;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

/**
 * @brief  FLASH operation error interrupt callback
 * @param  ReturnValue The value saved in this parameter depends on the ongoing procedure
 *                 - Sectors Erase: Sector which has been erased (if 0xFFFFFFFF, it means that
 *                                  all the selected sectors have been erased)
 *                 - Program      : Address which was selected for data program
 *                 - Mass Erase   : No return value expected
 * @retval None
 **/
void HAL_FLASH_OperationErrorCallback(uint32_t ReturnValue)
{
   static uint16_t cnt = 0;
   cnt++;
   printf("flash error callback counter: %d\r\n", cnt);
}

//读取指定地址的半字(16位数据)
 uint8_t FLASH_ReadByte(uint32_t address)
 {
   return *(__IO uint8_t*)address;
 }
//读取指定地址的半字(16位数据)
 uint16_t FLASH_ReadHalfWord(uint32_t address)
 {
   return *(__IO uint16_t*)address;
 }
 //读取指定地址字(32位数据)
  uint32_t FLASH_ReadWord(uint32_t address)
  {
    return *(__IO uint32_t*)address;
  }
 //从指定地址开始读取多个16数据
 void FLASH_ReadMoreData(uint32_t startAddress,uint16_t *readData,uint16_t countToRead)
{
   uint16_t dataIndex;
   for(dataIndex=0;dataIndex<countToRead;dataIndex++)
   {
     readData[dataIndex]=FLASH_ReadHalfWord(startAddress+dataIndex*2);
   }
 }
 //从指定地址开始读取多个32数据
 void FLASH_ReadMoreData32(uint32_t startAddress,uint32_t *readData,uint32_t countToRead)
{
   uint32_t dataIndex;
   for(dataIndex=0;dataIndex<countToRead;dataIndex++)
   {
     readData[dataIndex]=FLASH_ReadWord(startAddress+dataIndex*4);
   }
 }

 void read_from_flash(uint32_t adrress,uint32_t *buf,uint32_t count_len)
  {

  //  uint8_t i;
	 FLASH_ReadMoreData32(adrress,buf,count_len);
    // for(i=0;i<count_len;i++)
	// printf("\nread_flash:0X%x\n",(int)buf[i]);
   }
 //flash固件校验
 static uint32_t checksum;//这里的checksum不能用于形参，会被内存释放掉

 uint16_t ReadSTM32checksum(uint32_t startaddress,uint32_t length)
 {


	 union data
	 {
	 	uint32_t flashdata;
	 	uint8_t  tab[4];
	 }databuffer;
 	 for(uint32_t dataIndex=0;dataIndex<(length/4);dataIndex++)
 	   {

 		   databuffer.flashdata=FLASH_ReadWord(startaddress+dataIndex*4);
 		   //debug message:
 		  // printf("\r\nread_flash:0X%x\n",databuffer.flashdata);
 		   for(uint8_t i=0;i<4;i++)
 			   //debug message:
 		  // printf("\r\ndatabuffer.tab[i]:0X%x\n",databuffer.tab[i]);
 			   checksum+=databuffer.tab[i];

 	   }
 	printf("\r\n checksum:0X%x\n",(int)checksum);
 	return checksum;
 }
 //读取指定地址的字(32位数据)
 //faddr:读地址
 //返回值:对应数据.
 static uint32_t STMFLASH_ReadWord(uint32_t faddr)
 {
 	return *(__IO uint32_t *)faddr;
 }
 //从指定地址开始读出指定长度的数据
 //ReadAddr:起始地址
 //pBuffer:数据指针
 //NumToRead:字(32位)数
 void STMFLASH_Read(uint32_t ReadAddr,uint32_t *pBuffer,uint32_t NumToRead)
 {
 	uint32_t i;
 	for(i=0;i<NumToRead;i++)
 	{
 		pBuffer[i]=STMFLASH_ReadWord(ReadAddr);//读取4个字节.
 		ReadAddr+=4;//偏移4个字节.
 	}
 }

